package main

import (
	"fmt"
	"io"
	"net"
	"sync"
)

// Server 创建模型
type Server struct {
	Ip        string
	Port      int
	OnlineMap map[string]*User
	mapLock   sync.RWMutex
	Message   chan string
}

// NewServer 创建对外的server接口 返回指针
func NewServer(ip string, port int) *Server {
	return &Server{
		Ip:        ip,
		Port:      port,
		OnlineMap: make(map[string]*User),
		Message:   make(chan string),
	}
}

// Start 启动服务器的接口
func (_this *Server) Start() {
	// socket listen
	listen, err := net.Listen("tcp", fmt.Sprintf("%s:%d", _this.Ip, _this.Port))
	if err != nil {
		fmt.Println("net listen err: ", err)
		return
	}
	fmt.Println("net listen start successful")
	// close listen socket
	defer func(listen net.Listener) {
		err := listen.Close()
		if err != nil {
			fmt.Println("listen close err: ", err)
		}
	}(listen)
	// 监听Message
	go _this.ListenMessage()
	for {
		// accept 建立连接
		conn, err := listen.Accept()
		if err != nil {
			fmt.Println("listen accept err: ", err)
			return
		}
		// do handler 开启携程
		go _this.Handler(conn)
	}
}

// Handler 业务处理
func (_this *Server) Handler(conn net.Conn) {
	// .. 当前连接的业务
	user := NewUser(conn)
	fmt.Println("用户 [ " + user.Name + " ] --> 建立连接成功")
	// 用户上线处理 将用户加入到OnlineMap
	_this.mapLock.Lock()
	_this.OnlineMap[user.Name] = user
	_this.mapLock.Unlock()
	// 广播当前用户上线消息
	_this.BroadCast(user, "已上线")
	// 接收客户端发送的消息
	go func() {
		buf := make([]byte, 4096)
		for {
			read, err := conn.Read(buf)
			if err != nil && err != io.EOF {
				fmt.Println("read msg err: ", err)
				return
			}
			// 客户端关闭
			if read == 0 {
				_this.BroadCast(user, "下线")
				return
			}
			// 读取用户发送的消息 去除\n
			msg := string(buf[:read-1])
			_this.BroadCast(user, msg)
		}
	}()
	// 阻塞handler
	select {}
}

// BroadCast 广播消息
func (_this *Server) BroadCast(user *User, msg string) {
	sendMsg := "[ " + user.Addr + " ]" + user.Name + ":" + msg
	_this.Message <- sendMsg
}

// ListenMessage 监听广播消息 channel的goroutine , 一旦有消息就发送给所有在线的user
func (_this *Server) ListenMessage() {
	for {
		msg := <-_this.Message
		// 将消息发送给所有在线的user
		_this.mapLock.Lock()
		for _, client := range _this.OnlineMap {
			client.C <- msg
		}
		_this.mapLock.Unlock()
	}
}
